പൈത്തണിലെ കൺകറന്റ് പ്രോഗ്രാമിംഗിൻ്റെ ശക്തി പ്രയോജനപ്പെടുത്തുക. ഉയർന്ന പ്രകടനമുള്ള, സ്കേലബിൾ ആപ്ലിക്കേഷനുകൾ നിർമ്മിക്കാൻ Asyncio ടാസ്ക്കുകൾ എങ്ങനെ സൃഷ്ടിക്കാമെന്നും കൈകാര്യം ചെയ്യാമെന്നും റദ്ദാക്കാമെന്നും പഠിക്കുക.
പൈത്തൺ Asyncio മാസ്റ്റർ ചെയ്യുക: ടാസ്ക് സൃഷ്ടിക്കുന്നതിലും കൈകാര്യം ചെയ്യുന്നതിലും ഒരു ആഴത്തിലുള്ള പഠനം
ആധുനിക സോഫ്റ്റ്വെയർ ഡെവലപ്മെൻ്റ് ലോകത്ത്, പ്രകടനം പരമപ്രധാനമാണ്. ആയിരക്കണക്കിന് കൺകറന്റ് നെറ്റ്വർക്ക് കണക്ഷനുകൾ, ഡാറ്റാബേസ് ക്വറികൾ, API കോളുകൾ എന്നിവ യാതൊരു ബുദ്ധിമുട്ടും കൂടാതെ കൈകാര്യം ചെയ്യാൻ ആപ്ലിക്കേഷനുകൾക്ക് കഴിയണം. I/O-ബൗണ്ട് ഓപ്പറേഷനുകൾക്ക്—പ്രോഗ്രാം ഒരു നെറ്റ്വർക്ക് അല്ലെങ്കിൽ ഡിസ്ക് പോലുള്ള ബാഹ്യ വിഭവങ്ങൾക്കായി കാത്തിരിക്കുന്നതിൽ കൂടുതൽ സമയം ചിലവഴിക്കുന്നിടത്ത്—പരമ്പരാഗത സിൻക്രണസ് കോഡ് ഒരു പ്രധാന തടസ്സമാകും. ഇവിടെയാണ് അസിൻക്രണസ് പ്രോഗ്രാമിംഗ് തിളങ്ങുന്നത്, പൈത്തണിൻ്റെ asyncio
ലൈബ്രറി ഈ ശക്തി അൺലോക്ക് ചെയ്യുന്നതിനുള്ള താക്കോലാണ്.
asyncio
-യുടെ കൺകറൻസി മോഡലിൻ്റെ ഏറ്റവും ഹൃദയഭാഗത്ത് ലളിതവും എന്നാൽ ശക്തവുമായ ഒരു ആശയമുണ്ട്: Task. കോറൂട്ടീനുകൾ എന്തു ചെയ്യണം എന്ന് നിർവചിക്കുമ്പോൾ, ടാസ്ക്കുകളാണ് യഥാർത്ഥത്തിൽ കാര്യങ്ങൾ ചെയ്യുന്നത്. അവ കൺകറന്റ് എക്സിക്യൂഷൻ്റെ അടിസ്ഥാന യൂണിറ്റാണ്, നിങ്ങളുടെ പൈത്തൺ പ്രോഗ്രാമുകൾക്ക് ഒന്നിലധികം ഓപ്പറേഷനുകൾ ഒരുമിച്ച് കൈകാര്യം ചെയ്യാൻ അനുവദിക്കുന്നു, ഇത് ത്രൂപുട്ടും പ്രതികരണശേഷിയും ഗണ്യമായി മെച്ചപ്പെടുത്തുന്നു.
ഈ സമഗ്ര ഗൈഡ് നിങ്ങളെ asyncio.Task
-ലേക്ക് ഒരു ആഴത്തിലുള്ള പഠനത്തിലേക്ക് കൊണ്ടുപോകും. സൃഷ്ടിക്കലിൻ്റെ അടിസ്ഥാനകാര്യങ്ങൾ മുതൽ നൂതന മാനേജ്മെൻ്റ് പാറ്റേണുകൾ, റദ്ദാക്കൽ, മികച്ച സമ്പ്രദായങ്ങൾ വരെ ഞങ്ങൾ എല്ലാം വിശദീകരിക്കും. നിങ്ങൾ ഉയർന്ന ട്രാഫിക് ഉള്ള ഒരു വെബ് സർവീസ്, ഡാറ്റാ സ്ക്രാപ്പിംഗ് ടൂൾ, അല്ലെങ്കിൽ റിയൽ-ടൈം ആപ്ലിക്കേഷൻ നിർമ്മിക്കുകയാണെങ്കിലും, ഏത് ആധുനിക പൈത്തൺ ഡെവലപ്പർക്കും ടാസ്ക്കുകൾ മാസ്റ്റർ ചെയ്യുന്നത് ഒരു അത്യാവശ്യ വൈദഗ്ധ്യമാണ്.
എന്താണ് ഒരു കോറൂട്ടീൻ? ഒരു ദ്രുത ഓർമ്മപ്പെടുത്തൽ
നമുക്ക് ഓടുന്നതിന് മുമ്പ്, നടക്കണം. asyncio
ലോകത്ത്, നടത്തം കോറൂട്ടീനുകളെ മനസ്സിലാക്കുക എന്നതാണ്. ഒരു കോറൂട്ടീൻ എന്നത് async def
ഉപയോഗിച്ച് നിർവചിക്കപ്പെട്ട ഒരു പ്രത്യേകതരം ഫംഗ്ഷനാണ്.
ഒരു സാധാരണ പൈത്തൺ ഫംഗ്ഷൻ വിളിക്കുമ്പോൾ, അത് തുടക്കം മുതൽ അവസാനം വരെ പ്രവർത്തിക്കും. എന്നിരുന്നാലും, ഒരു കോറൂട്ടീൻ ഫംഗ്ഷൻ വിളിക്കുമ്പോൾ, അത് ഉടൻ പ്രവർത്തിക്കില്ല. പകരം, അത് ഒരു കോറൂട്ടീൻ ഒബ്ജക്റ്റ് തിരികെ നൽകും. ഈ ഒബ്ജക്റ്റ് ചെയ്യാനുള്ള ജോലിയുടെ ഒരു ബ്ലൂപ്രിൻ്റ് ആണ്, പക്ഷെ അത് സ്വന്തമായി നിഷ്ക്രിയമാണ്. ഇത് നിർത്താനും താൽക്കാലികമായി നിർത്താനും വീണ്ടും ആരംഭിക്കാനും കഴിയുന്ന ഒരു നിർത്തലാക്കിയ കണക്കുകൂട്ടലാണ്.
import asyncio
async def say_hello(name: str):
print(f"Preparing to greet {name}...")
await asyncio.sleep(1) # Simulate a non-blocking I/O operation
print(f"Hello, {name}!")
# Calling the function doesn't run it, it creates a coroutine object
coro = say_hello("World")
print(f"Created a coroutine object: {coro}")
# To actually run it, you need to use an entry point like asyncio.run()
# asyncio.run(coro)
മാന്ത്രിക പദം await
ആണ്. ഇത് ഇവൻ്റ് ലൂപ്പിനോട് പറയുന്നു, "ഈ പ്രവർത്തനം എടുക്കാൻ സമയമെടുത്തേക്കാം, അതിനാൽ എന്നെ ഇവിടെ നിർത്താനും മറ്റെന്തെങ്കിലും ചെയ്യാനും നിങ്ങൾക്ക് സ്വാതന്ത്യമുണ്ട്. ഈ പ്രവർത്തനം പൂർത്തിയാകുമ്പോൾ എന്നെ ഉണർത്തുക." നിർത്താനും സന്ദർഭങ്ങൾ മാറാനും ഉള്ള ഈ കഴിവാണ് കൺകറൻസി സാധ്യമാക്കുന്നത്.
കൺകറൻസിയുടെ ഹൃദയം: asyncio.Task മനസ്സിലാക്കുക
അതിനാൽ, ഒരു കോറൂട്ടീൻ ഒരു ബ്ലൂപ്രിൻ്റ് ആണ്. പാചകപ്പുരയോട് (ഇവൻ്റ് ലൂപ്പ്) പാചകം ചെയ്യാൻ തുടങ്ങാൻ എങ്ങനെയാണ് നമ്മൾ പറയുക? ഇത് asyncio.Task
വരുന്ന സ്ഥലമാണ്.
ഒരു asyncio.Task
എന്നത് ഒരു കോറൂട്ടീനെ പൊതിയുകയും asyncio ഇവൻ്റ് ലൂപ്പിൽ പ്രവർത്തിക്കാൻ ഷെഡ്യൂൾ ചെയ്യുകയും ചെയ്യുന്ന ഒരു ഒബ്ജക്റ്റ് ആണ്. ഇതിനെ ഇങ്ങനെ ചിന്തിക്കുക:
- Coroutine (
async def
): ഒരു വിഭവത്തിനായുള്ള വിശദമായ പാചകക്കുറിപ്പ്. - Event Loop: എല്ലാ പാചകവും നടക്കുന്ന കേന്ദ്ര അടുക്കള.
await my_coro()
: നിങ്ങൾ അടുക്കളയിൽ നിന്ന് പാചകക്കുറിപ്പ് ഘട്ടം ഘട്ടമായി സ്വയം പിന്തുടരുന്നു. വിഭവം പൂർത്തിയാകുന്നതുവരെ നിങ്ങൾക്ക് മറ്റൊന്നും ചെയ്യാൻ കഴിയില്ല. ഇത് സീക്വൻഷ്യൽ എക്സിക്യൂഷൻ ആണ്.asyncio.create_task(my_coro())
: നിങ്ങൾ പാചകക്കുറിപ്പ് അടുക്കളയിലെ ഒരു ഷെഫിന് (Task) കൈമാറി പറയുന്നു, "ഇതിൽ പ്രവർത്തിക്കാൻ തുടങ്ങുക." ഷെഫ് ഉടൻ തന്നെ തുടങ്ങും, നിങ്ങൾക്ക് മറ്റ് കാര്യങ്ങൾ ചെയ്യാൻ സ്വാതന്ത്ര്യമുണ്ട്, കൂടുതൽ പാചകക്കുറിപ്പുകൾ നൽകുന്നത് പോലെ. ഇത് കൺകറൻ്റ് എക്സിക്യൂഷൻ ആണ്.
പ്രധാന വ്യത്യാസം എന്തെന്നാൽ asyncio.create_task()
കോറൂട്ടീൻ "ബാക്ക്ഗ്രൗണ്ടിൽ" പ്രവർത്തിക്കാൻ ഷെഡ്യൂൾ ചെയ്യുകയും ഉടൻ തന്നെ നിങ്ങളുടെ കോഡിലേക്ക് നിയന്ത്രണം തിരികെ നൽകുകയും ചെയ്യുന്നു. നിങ്ങൾക്ക് ഒരു Task
ഒബ്ജക്റ്റ് തിരികെ ലഭിക്കും, അത് ഈ നടന്നുകൊണ്ടിരിക്കുന്ന പ്രവർത്തനത്തിൻ്റെ ഒരു ഹാൻഡിൽ ആയി പ്രവർത്തിക്കുന്നു. അതിൻ്റെ സ്റ്റാറ്റസ് പരിശോധിക്കാനോ, അത് റദ്ദാക്കാനോ, അല്ലെങ്കിൽ പിന്നീട് അതിൻ്റെ ഫലം കാത്തിരിക്കാനോ നിങ്ങൾക്ക് ഈ ഹാൻഡിൽ ഉപയോഗിക്കാം.
നിങ്ങളുടെ ആദ്യ ടാസ്ക്കുകൾ സൃഷ്ടിക്കുന്നു: `asyncio.create_task()` ഫംഗ്ഷൻ
ടാസ്ക് സൃഷ്ടിക്കാനുള്ള പ്രാഥമിക മാർഗ്ഗം asyncio.create_task()
ഫംഗ്ഷൻ ഉപയോഗിക്കുക എന്നതാണ്. ഇത് ഒരു കോറൂട്ടീൻ ഒബ്ജക്റ്റിനെ അതിൻ്റെ ആർഗ്യുമെൻ്റായി എടുക്കുകയും പ്രവർത്തിക്കാൻ ഷെഡ്യൂൾ ചെയ്യുകയും ചെയ്യുന്നു.
അടിസ്ഥാന സിൻ്റാക്സ്
ഉപയോഗം ലളിതമാണ്:
import asyncio
async def my_background_work():
print("Starting background work...")
await asyncio.sleep(2)
print("Background work finished.")
return "Success"
async def main():
print("Main function started.")
# Schedule my_background_work to run concurrently
task = asyncio.create_task(my_background_work())
# While the task runs, we can do other things
print("Task created. Main function continues to run.")
await asyncio.sleep(1)
print("Main function did some other work.")
# Now, wait for the task to complete and get its result
result = await task
print(f"Task completed with result: {result}")
asyncio.run(main())
task
സൃഷ്ടിച്ചതിന് ശേഷം ഉടൻ തന്നെ `main` ഫംഗ്ഷൻ അതിൻ്റെ പ്രവർത്തനം തുടരുന്നതായി ഔട്ട്പുട്ട് കാണിക്കുന്നത് ശ്രദ്ധിക്കുക. അത് തടയുന്നില്ല. അവസാനം ഞങ്ങൾ വ്യക്തമായി `await task` ചെയ്യുന്നതുവരെ മാത്രമേ അത് താൽക്കാലികമായി നിർത്തൂ.
ഒരു പ്രായോഗിക ഉദാഹരണം: കൺകറൻ്റ് വെബ് അഭ്യർത്ഥനകൾ
ഒരു സാധാരണ സാഹചര്യത്തിൽ ടാസ്ക്കുകളുടെ യഥാർത്ഥ ശക്തി നമുക്ക് കാണാം: ഒന്നിലധികം URL-കളിൽ നിന്ന് ഡാറ്റ ശേഖരിക്കുന്നത്. ഇതിനായി, ഞങ്ങൾ ജനപ്രിയ `aiohttp` ലൈബ്രറി ഉപയോഗിക്കും, ഇത് നിങ്ങൾക്ക് `pip install aiohttp` ഉപയോഗിച്ച് ഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയും.
ആദ്യം, സീക്വൻഷ്യൽ (പതുഗതിയുള്ള) രീതി നോക്കാം:
import asyncio
import aiohttp
import time
async def fetch_status(session, url):
async with session.get(url) as response:
return response.status
async def main_sequential():
urls = [
"https://www.python.org",
"https://www.google.com",
"https://www.github.com",
"https://www.microsoft.com"
]
start_time = time.time()
async with aiohttp.ClientSession() as session:
for url in urls:
status = await fetch_status(session, url)
print(f"Status for {url}: {status}")
end_time = time.time()
print(f"Sequential execution took {end_time - start_time:.2f} seconds")
# To run this, you would use: asyncio.run(main_sequential())
ഓരോ അഭ്യർത്ഥനയും ഏകദേശം 0.5 സെക്കൻഡ് എടുക്കുകയാണെങ്കിൽ, മൊത്തം സമയം ഏകദേശം 2 സെക്കൻഡ് ആയിരിക്കും, കാരണം ഓരോ `await` ഉം ആ ഒരൊറ്റ അഭ്യർത്ഥന പൂർത്തിയാകുന്നതുവരെ ലൂപ്പ് തടയുന്നു.
ഇനി, ടാസ്ക്കുകൾ ഉപയോഗിച്ച് കൺകറൻസിയുടെ ശക്തി അഴിച്ചുവിടാം:
import asyncio
import aiohttp
import time
# fetch_status coroutine remains the same
async def fetch_status(session, url):
async with session.get(url) as response:
return response.status
async def main_concurrent():
urls = [
"https://www.python.org",
"https://www.google.com",
"https://www.github.com",
"https://www.microsoft.com"
]
start_time = time.time()
async with aiohttp.ClientSession() as session:
# Create a list of tasks, but don't await them yet
tasks = [asyncio.create_task(fetch_status(session, url)) for url in urls]
# Now, wait for all tasks to complete
statuses = await asyncio.gather(*tasks)
for url, status in zip(urls, statuses):
print(f"Status for {url}: {status}")
end_time = time.time()
print(f"Concurrent execution took {end_time - start_time:.2f} seconds")
asyncio.run(main_concurrent())
കൺകറൻ്റ് പതിപ്പ് പ്രവർത്തിപ്പിക്കുമ്പോൾ, നിങ്ങൾ ഒരു വലിയ വ്യത്യാസം കാണും. മൊത്തം സമയം എല്ലാ അഭ്യർത്ഥനകളുടെയും സമ്മിശ്രണം എന്നതിലുപരി, ഏറ്റവും ദൈർഘ്യമേറിയ ഒറ്റ അഭ്യർത്ഥനയുടെ സമയമായിരിക്കും. കാരണം, ആദ്യത്തെ `fetch_status` കോറൂട്ടീൻ അതിൻ്റെ `await session.get(url)`-ൽ എത്തുമ്പോൾ തന്നെ, ഇവൻ്റ് ലൂപ്പ് അതിനെ താൽക്കാലികമായി നിർത്തുകയും ഉടൻ തന്നെ അടുത്തത് ആരംഭിക്കുകയും ചെയ്യുന്നു. എല്ലാ നെറ്റ്വർക്ക് അഭ്യർത്ഥനകളും ഫലപ്രദമായി ഒരേ സമയം സംഭവിക്കുന്നു.
ഒരു കൂട്ടം ടാസ്ക്കുകൾ കൈകാര്യം ചെയ്യുന്നു: അത്യാവശ്യ പാറ്റേണുകൾ
വ്യക്തിഗത ടാസ്ക്കുകൾ സൃഷ്ടിക്കുന്നത് നല്ലതാണ്, എന്നാൽ യഥാർത്ഥ ലോക ആപ്ലിക്കേഷനുകളിൽ, നിങ്ങൾക്ക് അവയെ സമാരംഭിച്ച്, കൈകാര്യം ചെയ്ത്, സമന്വയിപ്പിക്കേണ്ടതുണ്ട്. `asyncio` ഇതിനായി നിരവധി ശക്തമായ ഉപകരണങ്ങൾ നൽകുന്നു.
ആധുനിക സമീപനം (Python 3.11+): `asyncio.TaskGroup`
Python 3.11-ൽ അവതരിപ്പിച്ച, `TaskGroup` എന്നത് അനുബന്ധ ടാസ്ക്കുകളുടെ ഒരു കൂട്ടം കൈകാര്യം ചെയ്യാനുള്ള പുതിയതും ശുപാർശ ചെയ്യുന്നതും ഏറ്റവും സുരക്ഷിതവുമായ മാർഗ്ഗമാണ്. ഇത് സ്ട്രക്ച്ചേർഡ് കൺകറൻസി എന്ന് അറിയപ്പെടുന്നവ നൽകുന്നു.
`TaskGroup`-ൻ്റെ പ്രധാന സവിശേഷതകൾ:
- ഉറപ്പുനൽകിയ ക്ലീനപ്പ്: അതിനകത്ത് സൃഷ്ടിച്ച എല്ലാ ടാസ്ക്കുകളും പൂർത്തിയാകുന്നതുവരെ `async with` ബ്ലോക്ക് പുറത്തുകടക്കില്ല.
- ശക്തമായ പിശക് കൈകാര്യം ചെയ്യൽ: ഗ്രൂപ്പിലെ ഏതെങ്കിലും ടാസ്ക്ക് ഒരു exception ഉയർത്തിയാൽ, ഗ്രൂപ്പിലെ മറ്റെല്ലാ ടാസ്ക്കുകളും യാന്ത്രികമായി റദ്ദാക്കപ്പെടും, കൂടാതെ `async with` ബ്ലോക്കിൽ നിന്ന് പുറത്തുകടക്കുമ്പോൾ exception (`ExceptionGroup` ആണെങ്കിൽ) വീണ്ടും ഉയർത്തപ്പെടും. ഇത് ഉപേക്ഷിക്കപ്പെട്ട ടാസ്ക്കുകൾ തടയുകയും ഒരു പ്രവചനാതീതമായ അവസ്ഥ ഉറപ്പാക്കുകയും ചെയ്യുന്നു.
ഇത് എങ്ങനെ ഉപയോഗിക്കാം എന്നത് ഇതാ:
import asyncio
async def worker(delay):
print(f"Worker starting, will sleep for {delay}s")
await asyncio.sleep(delay)
# This worker will fail
if delay == 2:
raise ValueError("Something went wrong in worker 2")
print(f"Worker with delay {delay} finished")
return f"Result from {delay}s"
async def main():
print("Starting main with TaskGroup...")
try:
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(worker(1))
task2 = tg.create_task(worker(2)) # This one will fail
task3 = tg.create_task(worker(3))
print("Tasks created in the group.")
# This part of the code will NOT be reached if an exception occurs
# The results would be accessed via task1.result(), etc.
print("All tasks completed successfully.")
except* ValueError as eg: # Note the `except*` for ExceptionGroup
print(f"Caught an exception group with {len(eg.exceptions)} exceptions.")
for exc in eg.exceptions:
print(f" - {exc}")
print("Main function finished.")
asyncio.run(main())
ഇത് പ്രവർത്തിപ്പിക്കുമ്പോൾ, `worker(2)` ഒരു പിശക് ഉയർത്തുന്നത് നിങ്ങൾ കാണും. `TaskGroup` ഇത് പിടിക്കുന്നു, മറ്റ് പ്രവർത്തിക്കുന്ന ടാസ്ക്കുകളെ ( `worker(3)` പോലുള്ളവ) റദ്ദാക്കുന്നു, തുടർന്ന് `ValueError` അടങ്ങിയ ഒരു `ExceptionGroup` ഉയർത്തുന്നു. വിശ്വസനീയമായ സിസ്റ്റങ്ങൾ നിർമ്മിക്കുന്നതിന് ഈ പാറ്റേൺ അവിശ്വസനീയമാംവിധം ശക്തമാണ്.
ക്ലാസിക് വർക്ക്ഹോഴ്സ്: `asyncio.gather()`
`TaskGroup`-ന് മുമ്പ്, ഒന്നിലധികം awaitables ഒരുമിച്ച് പ്രവർത്തിപ്പിക്കാനും അവയെല്ലാം പൂർത്തിയാകുന്നതുവരെ കാത്തിരിക്കാനുമുള്ള ഏറ്റവും സാധാരണ മാർഗ്ഗം `asyncio.gather()` ആയിരുന്നു.
`gather()` കോറൂട്ടീനുകളുടെയോ ടാസ്ക്കുകളുടെയോ ഒരു ശ്രേണി എടുക്കുന്നു, അവയെല്ലാം പ്രവർത്തിപ്പിക്കുന്നു, ഇൻപുട്ടുകളുടെ അതേ ക്രമത്തിൽ അവയുടെ ഫലങ്ങളുടെ ഒരു ലിസ്റ്റ് തിരികെ നൽകുന്നു. "ഈ കാര്യങ്ങളെല്ലാം പ്രവർത്തിപ്പിക്കുക, എനിക്ക് എല്ലാ ഫലങ്ങളും നൽകുക" എന്നതിൻ്റെ സാധാരണ സാഹചര്യത്തിന് ഇത് ഒരു ഉയർന്ന തലത്തിലുള്ള, സൗകര്യപ്രദമായ ഫംഗ്ഷൻ ആണ്.
import asyncio
async def fetch_data(source, delay):
print(f"Fetching from {source}...")
await asyncio.sleep(delay)
return {"source": source, "data": f"some data from {source}"}
async def main():
# gather can take coroutines directly
results = await asyncio.gather(
fetch_data("API", 2),
fetch_data("Database", 3),
fetch_data("Cache", 1)
)
print(results)
asyncio.run(main())
`gather()` ഉപയോഗിച്ച് പിശക് കൈകാര്യം ചെയ്യൽ: ഡിഫോൾട്ടായി, `gather()`-ലേക്ക് നൽകിയിട്ടുള്ള awaitables-ൽ ഏതെങ്കിലും ഒരു exception ഉയർത്തിയാൽ, `gather()` ഉടൻ തന്നെ ആ exception പ്രചരിപ്പിക്കും, കൂടാതെ മറ്റ് പ്രവർത്തിക്കുന്ന ടാസ്ക്കുകൾ റദ്ദാക്കപ്പെടും. നിങ്ങൾക്ക് `return_exceptions=True` ഉപയോഗിച്ച് ഈ സ്വഭാവം മാറ്റാൻ കഴിയും. ഈ മോഡിൽ, exception ഉയർത്തുന്നതിന് പകരം, അത് ബന്ധപ്പെട്ട സ്ഥാനത്ത് ഫലങ്ങളുടെ ലിസ്റ്റിൽ സ്ഥാപിക്കും.
# ... inside main()
results = await asyncio.gather(
fetch_data("API", 2),
asyncio.create_task(worker(1)), # This will raise a ValueError
fetch_data("Cache", 1),
return_exceptions=True
)
# results will contain a mix of successful results and exception objects
print(results)
ഫൈൻ-ഗ്രെയിൻഡ് നിയന്ത്രണം: `asyncio.wait()`
`asyncio.wait()` എന്നത് ടാസ്ക്കുകളുടെ ഒരു കൂട്ടത്തിൽ കൂടുതൽ വിശദമായ നിയന്ത്രണം നൽകുന്ന ഒരു താഴ്ന്ന തലത്തിലുള്ള ഫംഗ്ഷൻ ആണ്. `gather()`-ൽ നിന്ന് വ്യത്യസ്തമായി, ഇത് ഫലങ്ങൾ നേരിട്ട് തിരികെ നൽകുന്നില്ല. പകരം, അത് ടാസ്ക്കുകളുടെ രണ്ട് സെറ്റുകൾ തിരികെ നൽകുന്നു: `done` ഉം `pending` ഉം.
അതിൻ്റെ ഏറ്റവും ശക്തമായ സവിശേഷത `return_when` പരാമീറ്റർ ആണ്, അത് ഇവയായിരിക്കാം:
asyncio.ALL_COMPLETED
(default): എല്ലാ ടാസ്ക്കുകളും തീർന്നു കഴിയുമ്പോൾ തിരികെ നൽകുന്നു.asyncio.FIRST_COMPLETED
: കുറഞ്ഞത് ഒരു ടാസ്ക്ക് പൂർത്തിയാകുന്നയുടൻ തിരികെ നൽകുന്നു.asyncio.FIRST_EXCEPTION
: ഒരു ടാസ്ക്ക് ഒരു exception ഉയർത്തുമ്പോൾ തിരികെ നൽകുന്നു. ഒരു ടാസ്ക്കും exception ഉയർത്തിയില്ലെങ്കിൽ, അത് `ALL_COMPLETED`-ന് തുല്യമാണ്.
ഒന്നിലധികം റീഡൻഡൻ്റ് ഡാറ്റാ ഉറവിടങ്ങൾ ക്വറി ചെയ്യുന്നതിനും പ്രതികരിക്കുന്ന ആദ്യത്തേത് ഉപയോഗിക്കുന്നതിനും സമാനമായ സാഹചര്യങ്ങൾക്ക് ഇത് വളരെ ഉപയോഗപ്രദമാണ്:
import asyncio
async def query_source(name, delay):
await asyncio.sleep(delay)
return f"Result from {name}"
async def main():
tasks = [
asyncio.create_task(query_source("Fast Mirror", 0.5)),
asyncio.create_task(query_source("Slow Main DB", 2.0)),
asyncio.create_task(query_source("Geographic Replica", 0.8))
]
done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
# Get the result from the completed task
first_result = done.pop().result()
print(f"Got first result: {first_result}")
# We now have pending tasks that are still running. It's crucial to clean them up!
print(f"Cancelling {len(pending)} pending tasks...")
for task in pending:
task.cancel()
# Await the cancelled tasks to allow them to process the cancellation
await asyncio.gather(*pending, return_exceptions=True)
print("Cleanup complete.")
asyncio.run(main())
TaskGroup vs. gather() vs. wait(): ഏത് എപ്പോൾ ഉപയോഗിക്കണം?
- `asyncio.TaskGroup` (Python 3.11+) ഉപയോഗിക്കുക നിങ്ങളുടെ ഡിഫോൾട്ട് തിരഞ്ഞെടുപ്പായി. അതിൻ്റെ സ്ട്രക്ച്ചേർഡ് കൺകറൻസി മോഡൽ ഒരു ലോജിക്കൽ പ്രവർത്തനത്തിൻ്റെ ഭാഗമായ ടാസ്ക്കുകളുടെ ഒരു കൂട്ടം കൈകാര്യം ചെയ്യുന്നതിന് സുരക്ഷിതവും വ്യക്തവും പിശകുകൾ കുറഞ്ഞതുമാണ്.
- `asyncio.gather()` ഉപയോഗിക്കുക സ്വതന്ത്ര ടാസ്ക്കുകളുടെ ഒരു കൂട്ടം പ്രവർത്തിപ്പിക്കുകയും അവയുടെ ഫലങ്ങളുടെ ഒരു ലിസ്റ്റ് നേടാൻ ആഗ്രഹിക്കുകയും ചെയ്യുമ്പോൾ. ഇത് ലളിതമായ സാഹചര്യങ്ങൾക്ക് വളരെ ഉപയോഗപ്രദവും അല്പം സംക്ഷിപ്തവുമാണ്, പ്രത്യേകിച്ച് 3.11-ന് മുമ്പുള്ള പൈത്തൺ പതിപ്പുകളിൽ.
- `asyncio.wait()` ഉപയോഗിക്കുക പൂർത്തീകരണ അവസ്ഥകളിൽ ഫൈൻ-ഗ്രെയിൻഡ് നിയന്ത്രണം ആവശ്യമുള്ള നൂതന സാഹചര്യങ്ങൾക്ക് (ഉദാഹരണത്തിന്, ആദ്യ ഫലം വരെ കാത്തിരിക്കുക) കൂടാതെ ശേഷിക്കുന്ന പെൻഡിംഗ് ടാസ്ക്കുകൾ സ്വമേധയാ കൈകാര്യം ചെയ്യാൻ തയ്യാറാകുമ്പോൾ.
ടാസ്ക്ക് ലൈഫ്സൈക്കിൾ & മാനേജ്മെൻ്റ്
ഒരു ടാസ്ക്ക് സൃഷ്ടിച്ചുകഴിഞ്ഞാൽ, നിങ്ങൾക്ക് Task
ഒബ്ജക്റ്റിലെ മെത്തേഡുകൾ ഉപയോഗിച്ച് അതിനോട് സംവദിക്കാൻ കഴിയും.
ടാസ്ക്ക് സ്റ്റാറ്റസ് പരിശോധിക്കുന്നു
task.done()
: ടാസ്ക്ക് പൂർത്തിയായിട്ടുണ്ടെങ്കിൽ (വിജയകരമായി, ഒരു exception-ഉമായി, അല്ലെങ്കിൽ റദ്ദാക്കപ്പെട്ടാൽ) `True` തിരികെ നൽകുന്നു.task.cancelled()
: ടാസ്ക്ക് റദ്ദാക്കപ്പെട്ടെങ്കിൽ `True` തിരികെ നൽകുന്നു.task.exception()
: ടാസ്ക്ക് ഒരു exception ഉയർത്തിയെങ്കിൽ, ഇത് exception ഒബ്ജക്റ്റ് തിരികെ നൽകുന്നു. അല്ലെങ്കിൽ, അത് `None` തിരികെ നൽകുന്നു. ടാസ്ക്ക് `done()` ആയതിനു ശേഷം മാത്രമേ ഇത് വിളിക്കാൻ കഴിയൂ.
ഫലങ്ങൾ വീണ്ടെടുക്കുന്നു
ഒരു ടാസ്ക്കിൻ്റെ ഫലം നേടാനുള്ള പ്രധാന മാർഗ്ഗം അത് `await` ചെയ്യുക എന്നതാണ്. ടാസ്ക്ക് വിജയകരമായി പൂർത്തിയായിട്ടുണ്ടെങ്കിൽ, ഇത് മൂല്യം തിരികെ നൽകും. അത് ഒരു exception ഉയർത്തിയെങ്കിൽ, `await task` ആ exception വീണ്ടും ഉയർത്തും. അത് റദ്ദാക്കപ്പെട്ടെങ്കിൽ, `await task` ഒരു `CancelledError` ഉയർത്തും.
പ്രത്യക്ഷത്തിൽ, ഒരു ടാസ്ക്ക് `done()` ആണെന്ന് നിങ്ങൾക്ക് അറിയാമെങ്കിൽ, നിങ്ങൾക്ക് `task.result()` വിളിക്കാം. ഇത് മൂല്യങ്ങൾ തിരികെ നൽകുന്നതിലോ exceptions ഉയർത്തുന്നതിലോ `await task` ക്ക് തുല്യമായി പ്രവർത്തിക്കുന്നു.
റദ്ദാക്കലിൻ്റെ കല
ദീർഘനേരം പ്രവർത്തിക്കുന്ന പ്രവർത്തനങ്ങൾ മനോഹരമായി റദ്ദാക്കാനുള്ള കഴിവ് robuste ആയ ആപ്ലിക്കേഷനുകൾ നിർമ്മിക്കുന്നതിന് നിർണായകമാണ്. ഒരു ടൈംഔട്ട്, ഉപയോക്തൃ അഭ്യർത്ഥന, അല്ലെങ്കിൽ സിസ്റ്റത്തിലെ മറ്റൊരു സ്ഥലത്തെ പിശക് എന്നിവ കാരണം ഒരു ടാസ്ക്ക് റദ്ദാക്കേണ്ടി വന്നേക്കാം.
നിങ്ങളുടെ task.cancel()
മെത്തേഡ് വിളിക്കുന്നതിലൂടെ നിങ്ങൾക്ക് ഒരു ടാസ്ക്ക് റദ്ദാക്കാം. എന്നിരുന്നാലും, ഇത് ടാസ്ക്കിനെ ഉടൻ നിർത്തുന്നില്ല. പകരം, ഒരു CancelledError
exception കോറൂട്ടീണിനുള്ളിൽ അടുത്ത await
പോയിൻ്റിൽ എറിയാൻ ഷെഡ്യൂൾ ചെയ്യുന്നു. ഇത് ഒരു നിർണായക വിശദാംശമാണ്. ഇത് കോറൂട്ടീന് പുറത്തുകടക്കുന്നതിന് മുമ്പ് വൃത്തിയാക്കാൻ അവസരം നൽകുന്നു.
നന്നായി പെരുമാറുന്ന ഒരു കോറൂട്ടീൻ ഈ `CancelledError` മനോഹരമായി കൈകാര്യം ചെയ്യണം, സാധാരണയായി ഫയൽ ഹാൻഡിലുകൾ അല്ലെങ്കിൽ ഡാറ്റാബേസ് കണക്ഷനുകൾ പോലുള്ള വിഭവങ്ങൾ അടഞ്ഞുകിടക്കുന്നെന്ന് ഉറപ്പാക്കാൻ ഒരു `try...finally` ബ്ലോക്ക് ഉപയോഗിക്കണം.
import asyncio
async def resource_intensive_task():
print("Acquiring resource (e.g., opening a connection)...")
try:
for i in range(10):
print(f"Working... step {i+1}")
await asyncio.sleep(1) # This is an await point where CancelledError can be injected
except asyncio.CancelledError:
print("Task was cancelled! Cleaning up...")
raise # It's good practice to re-raise CancelledError
finally:
print("Releasing resource (e.g., closing connection). This always runs.")
async def main():
task = asyncio.create_task(resource_intensive_task())
# Let it run for a bit
await asyncio.sleep(2.5)
print("Main decides to cancel the task.")
task.cancel()
try:
await task
except asyncio.CancelledError:
print("Main has confirmed the task was cancelled.")
asyncio.run(main())
`finally` ബ്ലോക്ക് പ്രവർത്തിക്കുമെന്ന് ഉറപ്പുനൽകുന്നു, ഇത് ക്ലീനപ്പ് ലോജിക്കിന് അനുയോജ്യമായ സ്ഥലമാക്കുന്നു.
ടൈംഔട്ട് ചേർക്കുന്നു `asyncio.timeout()` & `asyncio.wait_for()`
സ്വമേധയാ സ്ലീപ്പ് ചെയ്യുകയും റദ്ദാക്കുകയും ചെയ്യുന്നത് മടുപ്പിക്കുന്നതാണ്. `asyncio` ഈ സാധാരണ പാറ്റേണിന് ഹെൽപ്പർമാർ നൽകുന്നു.
Python 3.11+-ൽ, `asyncio.timeout()` കോൺടെക്സ്റ്റ് മാനേജർ ആണ് ഇഷ്ടപ്പെട്ട മാർഗ്ഗം:
async def long_running_operation():
await asyncio.sleep(10)
print("Operation finished")
async def main():
try:
async with asyncio.timeout(2): # Set a 2-second timeout
await long_running_operation()
except TimeoutError:
print("The operation timed out!")
asyncio.run(main())
പഴയ പൈത്തൺ പതിപ്പുകൾക്ക്, നിങ്ങൾക്ക് `asyncio.wait_for()` ഉപയോഗിക്കാം. ഇത് awaitable-നെ ഒരു ഫംഗ്ഷൻ കോളിൽ പൊതിയുന്നു എന്നതിനാൽ സമാനമായി പ്രവർത്തിക്കുന്നു:
async def main_legacy():
try:
await asyncio.wait_for(long_running_operation(), timeout=2)
except asyncio.TimeoutError:
print("The operation timed out!")
asyncio.run(main_legacy())
രണ്ട് ടൂളുകളും ടൈംഔട്ട് എത്തുമ്പോൾ ആന്തരിക ടാസ്ക്കിനെ റദ്ദാക്കുന്നതിലൂടെ പ്രവർത്തിക്കുന്നു, ഒരു `TimeoutError` (ഇത് `CancelledError`-ൻ്റെ ഒരു സബ്ക്ലാസ് ആണ്) ഉയർത്തുന്നു.
സാധാരണ കെണികളും മികച്ച സമ്പ്രദായങ്ങളും
ടാസ്ക്കുകളുമായി പ്രവർത്തിക്കുന്നത് ശക്തമാണ്, എന്നാൽ ഒഴിവാക്കേണ്ട ചില സാധാരണ കെണികളുണ്ട്.
- കെണി: "ഫയർ ആൻഡ് ഫോർഗെറ്റ്" തെറ്റ്. `create_task` ഉപയോഗിച്ച് ഒരു ടാസ്ക്ക് സൃഷ്ടിക്കുകയും ഒരിക്കലും അതിനെ `await` ചെയ്യാതിരിക്കുകയോ (അല്ലെങ്കിൽ `TaskGroup` പോലുള്ള മാനേജർ) ചെയ്യാതിരിക്കുകയും ചെയ്യുന്നത് അപകടകരമാണ്. ആ ടാസ്ക്ക് ഒരു exception ഉയർത്തിയാൽ, exception നിശബ്ദമായി നഷ്ടപ്പെട്ടേക്കാം, കൂടാതെ ടാസ്ക്ക് അതിൻ്റെ ജോലി പൂർത്തിയാക്കുന്നതിന് മുമ്പ് നിങ്ങളുടെ പ്രോഗ്രാം പുറത്തുകടന്നേക്കാം. ഫലം `await` ചെയ്യാൻ ഉത്തരവാദിത്തമുള്ള എല്ലാ ടാസ്ക്കുകൾക്കും എപ്പോഴും ഒരു വ്യക്തമായ ഉടമയുണ്ടായിരിക്കണം.
- കെണി: `asyncio.run()` ഉം `create_task()` ഉം തമ്മിൽ ആശയക്കുഴപ്പത്തിലാകുന്നത്. `asyncio.run(my_coro())` എന്നത് ഒരു `asyncio` പ്രോഗ്രാം ആരംഭിക്കുന്നതിനുള്ള പ്രധാന എൻട്രി പോയിൻ്റ് ആണ്. ഇത് ഒരു പുതിയ ഇവൻ്റ് ലൂപ്പ് സൃഷ്ടിക്കുകയും നൽകിയിട്ടുള്ള കോറൂട്ടീൻ പൂർത്തിയാകുന്നതുവരെ പ്രവർത്തിപ്പിക്കുകയും ചെയ്യുന്നു. `asyncio.create_task(my_coro())` എന്നത് നിലവിൽ പ്രവർത്തിക്കുന്ന ഒരു async ഫംഗ്ഷന് ഉള്ളിൽ കൺകറൻ്റ് എക്സിക്യൂഷൻ ഷെഡ്യൂൾ ചെയ്യാൻ ഉപയോഗിക്കുന്നു.
- മികച്ച സമ്പ്രദായം: ആധുനിക പൈത്തണിന് `TaskGroup` ഉപയോഗിക്കുക. അതിൻ്റെ ഡിസൈൻ പല സാധാരണ പിശകുകളും, ഉപേക്ഷിക്കപ്പെട്ട ടാസ്ക്കുകളും കൈകാര്യം ചെയ്യാത്ത exceptions പോലുള്ളവയും തടയുന്നു. നിങ്ങൾ Python 3.11 അല്ലെങ്കിൽ അതിനുശേഷമുള്ള പതിപ്പുകളിലാണെങ്കിൽ, അതിനെ നിങ്ങളുടെ ഡിഫോൾട്ട് ചോയ്സ് ആക്കുക.
- മികച്ച സമ്പ്രദായം: നിങ്ങളുടെ ടാസ്ക്കുകൾക്ക് പേര് നൽകുക. ഒരു ടാസ്ക്ക് സൃഷ്ടിക്കുമ്പോൾ, `name` പരാമീറ്റർ ഉപയോഗിക്കുക: `asyncio.create_task(my_coro(), name='DataProcessor-123')`. ഇത് debugging-ന് വളരെ വിലപ്പെട്ടതാണ്. നിങ്ങൾ പ്രവർത്തിക്കുന്ന എല്ലാ ടാസ്ക്കുകളും ലിസ്റ്റ് ചെയ്യുമ്പോൾ, അർത്ഥവത്തായ പേരുകൾ നിങ്ങളുടെ പ്രോഗ്രാം എന്തുചെയ്യുന്നു എന്ന് മനസ്സിലാക്കാൻ സഹായിക്കുന്നു.
- മികച്ച സമ്പ്രദായം: മനോഹരമായ ഷട്ട്ഡൗൺ ഉറപ്പാക്കുക. നിങ്ങളുടെ ആപ്ലിക്കേഷൻ ഷട്ട്ഡൗൺ ചെയ്യേണ്ടി വരുമ്പോൾ, എല്ലാ പ്രവർത്തിക്കുന്ന പശ്ചാത്തല ടാസ്ക്കുകളെയും റദ്ദാക്കാനും അവയെല്ലാം മനോഹരമായി വൃത്തിയാക്കുന്നതിനായി കാത്തിരിക്കാനും ഒരു സംവിധാനം ഉണ്ടായിരിക്കണം.
നൂതന ആശയങ്ങൾ: ഒരു എത്തിനോട്ടം
Debugging ഉം introspection ഉം വേണ്ടി, `asyncio` രണ്ട് ഉപയോഗപ്രദമായ ഫംഗ്ഷനുകൾ നൽകുന്നു:
asyncio.current_task()
: നിലവിൽ പ്രവർത്തിക്കുന്ന കോഡിനായുള്ള `Task` ഒബ്ജക്റ്റ് തിരികെ നൽകുന്നു.asyncio.all_tasks()
: ഇവൻ്റ് ലൂപ്പ് കൈകാര്യം ചെയ്യുന്ന നിലവിലെ എല്ലാ `Task` ഒബ്ജക്റ്റുകളുടെയും ഒരു സെറ്റ് തിരികെ നൽകുന്നു. നിങ്ങളുടെ പ്രോഗ്രാം എന്താണ് ചെയ്യുന്നതെന്ന് മനസ്സിലാക്കാൻ debugging-ന് ഇത് വളരെ ഉപയോഗപ്രദമാണ്.
ടാസ്ക്കുകളിൽ പൂർത്തീകരണ കോൾബാക്കുകൾ ചേർക്കാൻ നിങ്ങൾക്ക് `task.add_done_callback()` ഉപയോഗിക്കാം. ഇത് ഉപയോഗപ്രദമാണെങ്കിലും, ഇത് പലപ്പോഴും കൂടുതൽ സങ്കീർണ്ണമായ, കോൾബാക്ക്-സ്റ്റൈൽ കോഡ് ഘടനയിലേക്ക് നയിക്കുന്നു. `await`, `TaskGroup`, അല്ലെങ്കിൽ `gather` പോലുള്ള ആധുനിക സമീപനങ്ങൾ സാധാരണയായി വായിക്കാനുള്ളതും പരിപാലിക്കാനുള്ളതും ഇഷ്ടപ്പെടുന്നു.
ഉപസംഹാരം
asyncio.Task
എന്നത് ആധുനിക പൈത്തണിലെ കൺകറൻസിയുടെ എഞ്ചിൻ ആണ്. ടാസ്ക്കുകളുടെ സൃഷ്ടിക്കൽ, മാനേജ്മെൻ്റ്, അവയുടെ ലൈഫ്സൈക്കിൾ മനോഹരമായി കൈകാര്യം ചെയ്യൽ എന്നിവ എങ്ങനെ ചെയ്യാമെന്ന് മനസ്സിലാക്കുന്നതിലൂടെ, നിങ്ങളുടെ I/O-ബൗണ്ട് ആപ്ലിക്കേഷനുകളെ പതുഗതിയുള്ള, സീക്വൻഷ്യൽ പ്രോസസ്സുകളിൽ നിന്ന് വളരെ കാര്യക്ഷമമായ, സ്കേലബിൾ, പ്രതികരണശേഷിയുള്ള സിസ്റ്റങ്ങളിലേക്ക് മാറ്റാൻ കഴിയും.
create_task()` ഉപയോഗിച്ച് ഒരു കോറൂട്ടീൻ ഷെഡ്യൂൾ ചെയ്യുന്നതിൻ്റെ അടിസ്ഥാന ആശയത്തിൽ നിന്ന്, `TaskGroup`, `gather()`, `wait()` എന്നിവ ഉപയോഗിച്ച് സങ്കീർണ്ണമായ വർക്ക്ഫ്ലോകൾ ഓർക്കസ്ട്രേറ്റ് ചെയ്യുന്നത് വരെയുള്ള യാത്ര ഞങ്ങൾ ഉൾക്കൊള്ളുന്നു. robuste ആയ സോഫ്റ്റ്വെയർ നിർമ്മിക്കുന്നതിന് ശക്തമായ error handling, cancellation, timeout എന്നിവയുടെ നിർണായക പ്രാധാന്യവും ഞങ്ങൾ വിശദീകരിച്ചിട്ടുണ്ട്.
അസിൻക്രണസ് പ്രോഗ്രാമിംഗിൻ്റെ ലോകം വിശാലമാണ്, എന്നാൽ ടാസ്ക്കുകൾ മാസ്റ്റർ ചെയ്യുന്നത് നിങ്ങൾക്ക് എടുക്കാൻ കഴിയുന്ന ഏറ്റവും പ്രധാനപ്പെട്ട ചുവടുവെപ്പാണ്. പരീക്ഷിക്കാൻ തുടങ്ങുക. നിങ്ങളുടെ ആപ്ലിക്കേഷനിലെ ഒരു സീക്വൻഷ്യൽ, I/O-ബൗണ്ട് ഭാഗം കൺകറൻ്റ് ടാസ്ക്കുകൾ ഉപയോഗിക്കാൻ മാറ്റുക, അതിൻ്റെ പ്രകടനം മെച്ചപ്പെടുന്നത് സ്വയം കാണുക. കൺകറൻസിയുടെ ശക്തി സ്വീകരിക്കുക, അടുത്ത തലമുറയിലെ ഉയർന്ന പ്രകടനമുള്ള പൈത്തൺ ആപ്ലിക്കേഷനുകൾ നിർമ്മിക്കാൻ നിങ്ങൾ നന്നായി സജ്ജരായിരിക്കും.